package com.linko.soundrecorder;

import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.StatFs;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

public class RecordService extends Service implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {
    private final String TAG = RecordService.class.getSimpleName();
    private static MediaRecorder mRecorder;
    private final int AUDIO_CHANNEL = 1;
    private PowerManager.WakeLock mWakeLock;
    private File mRecordingFile;

    private static long mStartTime = 0;
    private static String mFilePath = null;
    private NotificationManager mNotificationManager;
    private final int NOTIFICATION_ID = 1;
    public final static String RECORDER_SERVICE_BROADCAST_NAME = "com.linko.soundrecorder.broadcast";
    public final static String RECORDER_SERVICE_BROADCAST_STATE = "is_recording";
    public final static String RECORDER_SERVICE_BROADCAST_ERROR = "error_code";
    public final static String RECORDER_SERVICE_BROADCAST_PHONE_STATE = "phone_state";
    private TelephonyManager mTeleManager;
    private static String MONITOR_STORAGE_SIZE_ACTION = "cn.linko.action.STORAGE_SIZE";
    private static final int MONITOR_STORAGE_SPACE_INTERVAL = 2 * 60 * 1000;
    private final int RESERVE_SPACE = 50;//M
    private AlertDialog mDialog;
    private MonitorTaskReceiver mMonitorTaskReceiver;

    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            if (state != TelephonyManager.CALL_STATE_IDLE) {
                sendStateBroadcast();
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SoundRecorder");
        mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
        mTeleManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        mTeleManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
        registMonitorReceiver();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");
        if (intent == null) {

            // if recorder be killed foreground,not let the service start agin.
            // if restart,the recorder will re-record,so we do not sync correct
            // the state of record.
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }
        Bundle bundle = intent.getExtras();
        if (bundle != null && bundle.containsKey(Recorder.ACTION_NAME)) {
            switch (bundle.getInt(Recorder.ACTION_NAME, Recorder.ACTION_INVALID)) {
                case Recorder.ACTION_START_RECORDING: {
                    startRecord(bundle);
                    break;
                }
                case Recorder.ACTION_STOP_RECORDING: {
                    stopRecord();
                    break;
                }
                case Recorder.ACTION_SHOW_NOTI_START_RECORDING: {
                    showNotification();
                    break;
                }
                case Recorder.ACTION_HIDE_NOTI_START_RECORDING: {
                    hideNotification();
                    break;
                }
                case Recorder.ACTION_SHOW_NOTI_STOP_RECORDING: {
                    break;
                }
            }
            return START_STICKY;
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    private void startRecord(Bundle bundle) {
        Log.i(TAG, "start recording");
        if (bundle == null) {
            return;
        }
        int outputfileformat = bundle.getInt(Recorder.ACTION_PARAM_FORMAT);
        String path = bundle.getString(Recorder.ACTION_PARAM_SAMPLE_FILE);
        boolean highQuality = bundle.getBoolean(Recorder.ACTION_PARAM_HIGH_QUALITY);
        long limitSize = bundle.getLong(Recorder.ACTION_PARAM_MAX_FILE_SIZE);
        if (mRecorder == null) {
            if (mRecordingFile == null) {
                mRecordingFile = new File(path);
            }

            mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setAudioChannels(AUDIO_CHANNEL);
            if (outputfileformat == MediaRecorder.OutputFormat.THREE_GPP) {
                mRecorder.setAudioSamplingRate(highQuality ? 48000 : 24000);
                mRecorder.setOutputFormat(outputfileformat);
                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            } else {
                mRecorder.setAudioSamplingRate(highQuality ? 16000 : 8000);
                mRecorder.setOutputFormat(outputfileformat);
                mRecorder.setAudioEncoder(highQuality ? MediaRecorder.AudioEncoder.AMR_WB
                        : MediaRecorder.AudioEncoder.AMR_NB);
            }
            mRecorder.setOutputFile(path);
            mRecorder.setOnErrorListener(this);
            mRecorder.setOnInfoListener(this);
            try {
                mRecorder.prepare();
            } catch (IOException e) {
                Log.i(TAG, "prepare occur error!");
                e.printStackTrace();
                mRecorder.reset();
                mRecorder.release();
                mRecorder = null;
                sendErrorBroadcast(Recorder.INTERNAL_ERROR);
                return;
            }
            try {
                mRecorder.start();
            } catch (RuntimeException exception) {

                Log.i(TAG, "start occur error!");
                exception.printStackTrace();
                mRecorder.reset();
                mRecorder.release();
                mRecorder = null;
                stopSelf();
                sendErrorBroadcast(Recorder.INTERNAL_ERROR);
                return;
            }
            mWakeLock.acquire();
            mStartTime = System.currentTimeMillis();
            mFilePath = path;
            sendStateBroadcast();
            monitorStorageSpaceTask();
        }
    }

    private void registMonitorReceiver() {
        IntentFilter logTaskFilter = new IntentFilter();
        logTaskFilter.addAction(MONITOR_STORAGE_SIZE_ACTION);
        mMonitorTaskReceiver = new MonitorTaskReceiver();
        registerReceiver(mMonitorTaskReceiver, logTaskFilter);
    }

    private void unRegistMonitorReceiver() {
        if (mMonitorTaskReceiver != null) {
            unregisterReceiver(mMonitorTaskReceiver);
        }
    }

    class MonitorTaskReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (MONITOR_STORAGE_SIZE_ACTION.equals(action)) {
                long availableSpace = getStorageAvailableSize();
                Log.i(TAG, "available Space:" + availableSpace);
                if (availableSpace <= RESERVE_SPACE && isRecording()) {
                    String path = SettingsActivity.getPrefStoragePath(context);
                    boolean isMounted = StorageHelper.isVolumeMounted(path);
                    if (isMounted) {
                        return;
                    }
                    if (availableSpace <= RESERVE_SPACE) {
                        showAlertDialog();
                    } else if (availableSpace <= 20) {
                        stopRecord();
                        if (mDialog != null && mDialog.isShowing()) {
                            mDialog.dismiss();
                        }
                    }
                }
            }
        }
    }

    private void showAlertDialog() {
        if (null != mDialog && mDialog.isShowing()) {
            return;
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
        builder.setTitle(getString(R.string.warm_no_space_dialog_title)).setMessage(
                R.string.insufficient_storage_message);
        builder.setNegativeButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                stopRecord();
            }
        });
        mDialog = builder.create();
        mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        mDialog.show();
    }

    /**
     * @return avalia space M.
     */
    public long getStorageAvailableSize() {
        StatFs statFs = new StatFs(SettingsActivity.getPrefStoragePath(getApplicationContext()));
        long blocSize = statFs.getBlockSize();
        long availaBlock = statFs.getAvailableBlocks();
        return availaBlock * blocSize / 1024 / 1024;
    }

    public static long getStartRecordTime() {
        return mStartTime;
    }

    public static String getFilePath() {
        return mFilePath;
    }

    @Override
    public void onDestroy() {
        if (mWakeLock.isHeld()) {
            mWakeLock.release();
        }
        mTeleManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
        unRegistMonitorReceiver();
        super.onDestroy();
    }

    private void stopRecord() {
        Log.i(TAG, "stop recording");
        hideNotification();
        if (mRecorder != null) {
            try {
                mRecorder.stop();
            } catch (RuntimeException e) {
                Log.i(TAG, "stop occur error!");
                e.printStackTrace();
            } finally {
                mRecorder.reset();
                mRecorder.release();
                mRecorder = null;
            }
        }
        cancelMonitorTask();
        sendStateBroadcast();
        stopSelf();
    }


    @Override
    public void onError(MediaRecorder mediaRecorder, int i, int i2) {
        sendErrorBroadcast(Recorder.INTERNAL_ERROR);
        stopRecord();
    }

    public static int getMaxAmplitude() {
        if (mRecorder != null) {
            return mRecorder.getMaxAmplitude();
        }
        return 0;
    }

    public static boolean isRecording() {
        return mRecorder != null;
    }

    public void showNotification() {
        Notification notification = new Notification();
        notification.icon = R.drawable.ic_launcher;
        notification.flags = Notification.FLAG_ONGOING_EVENT;
        PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), SoundRecorder.class), 0);
        notification.setLatestEventInfo(getApplicationContext(), getString(R.string.app_name),
                getString(R.string.notification_recording), pendingIntent);
        startForeground(NOTIFICATION_ID, notification);
    }

    public void hideNotification() {
        stopForeground(true);
    }

    private void sendStateBroadcast() {
        Intent intent = new Intent(RECORDER_SERVICE_BROADCAST_NAME);
        intent.putExtra(RECORDER_SERVICE_BROADCAST_STATE, mRecorder != null);
        intent.putExtra(RECORDER_SERVICE_BROADCAST_PHONE_STATE, mTeleManager.getCallState());
        sendBroadcast(intent);
    }

    private void sendErrorBroadcast(int error) {
        Intent intent = new Intent(RECORDER_SERVICE_BROADCAST_NAME);
        intent.putExtra(RECORDER_SERVICE_BROADCAST_ERROR, error);
        sendBroadcast(intent);
    }

    @Override
    public void onInfo(MediaRecorder mediaRecorder, int what, int i2) {
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
            Toast.makeText(getApplicationContext(), R.string.max_length_reached, Toast.LENGTH_LONG).show();
            stopRecord();
            sendStateBroadcast();
        }
    }

    private void monitorStorageSpaceTask() {
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent monitorStorageIntent = new Intent(MONITOR_STORAGE_SIZE_ACTION);
        PendingIntent monitorStorageSender = PendingIntent.getBroadcast(this, 0,
                monitorStorageIntent, 0);
        am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
                MONITOR_STORAGE_SPACE_INTERVAL, monitorStorageSender);
        Log.d(TAG, "Deploy monitor task succeed!");
    }

    private void cancelMonitorTask() {
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent monitorFileIntent = new Intent(MONITOR_STORAGE_SIZE_ACTION);
        PendingIntent monitorSizeSender = PendingIntent.getBroadcast(this, 0, monitorFileIntent, 0);
        am.cancel(monitorSizeSender);
        Log.d(TAG, "Cancel monitor task succeed!");
    }
}
